//
//  DBCountDown.swift
//  DBCA
//
//  Created by mugua on 2024/7/6.
//

import Foundation

public class DBCountDown: NSObject {
//    public var defaultTime: Int = 61
        public var defaultTime: Int = 10
    public let isRunning: BehaviorRelay<Bool> = BehaviorRelay(value: false)
    private lazy var residueTime = BehaviorRelay(value: defaultTime)
    
    private var timer: DispatchSourceTimer? = nil
    private var didEnterBackground: Disposable? = nil
    private var willEnterForeground: Disposable? = nil
    

    deinit {
        invalidate()
    }
}

extension DBCountDown {
    
    public func fire() -> Observable<String> {
        
        invalidate()
        
        isRunning.accept(true)
        
        var enterBackgroundDate: Date? = nil
        didEnterBackground = NotificationCenter.default.rx
            .notification(UIApplication.didEnterBackgroundNotification)
            .subscribe { _ in
                enterBackgroundDate = Date()
        }
        
        willEnterForeground = NotificationCenter.default.rx
            .notification(UIApplication.willEnterForegroundNotification)
            .subscribe { [unowned self] _ in
                let time = self.residueTime.value - Int(round(Date().timeIntervalSince(enterBackgroundDate!)))
                self.residueTime.accept(time)
        }
        
        
        let timer = DispatchSource.makeTimerSource(queue: .global())
        timer.schedule(deadline: .now(), repeating: .seconds(1))
        timer.setEventHandler { [weak self] in
            self?.updateResidueTime()
        }
        timer.resume()
        self.timer = timer
        
        return residueTime
            .withLatestFrom(Observable.just(defaultTime), resultSelector: { t, defaultTime -> String in
                return t == defaultTime ? "send code" : "\(t)s"
            })
            .share(replay: 1)
    }
    
    public func invalidate() {
        timer?.cancel()
        didEnterBackground?.dispose()
        willEnterForeground?.dispose()
    }
    
    private func updateResidueTime() {
        let time = residueTime.value - 1
        if time <= 0 {
            invalidate()
            isRunning.accept(false)
            residueTime.accept(defaultTime)
        } else {
            residueTime.accept(time)
        }
    }
}
